package com.jservice.web.smkSn;

import com.jfinal.plugin.activerecord.Db;
import com.jservice.framework.utils.FreemarkerUtils;
import freemarker.template.TemplateException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.io.IOException;

/**
 * Service - 序号
 *
 * @author cwvian
 * @version 1.0
 * @date 2017/3/1
 */
@Service("snService")
public class SnService implements InitializingBean {

	/**
	 * 类型
	 */
	public enum Type {

		/** 订单 */
		order,

		/** 收款单 */
		payment,

		/** 退款单 */
		refunds,

		/** 提现 */
		withdrawal
	}

	private HiloOptimizer orderHiloOptimizer;
	private HiloOptimizer paymentHiloOptimizer;
	private HiloOptimizer refundsHiloOptimizer;
	private HiloOptimizer withdrawalHiloOptimizer;

	@Value("${sn.order.prefix}")
	private String orderPrefix;
	@Value("${sn.order.maxLo}")
	private int orderMaxLo;
	@Value("${sn.payment.prefix}")
	private String paymentPrefix;
	@Value("${sn.payment.maxLo}")
	private int paymentMaxLo;
	@Value("${sn.refunds.prefix}")
	private String refundsPrefix;
	@Value("${sn.refunds.maxLo}")
	private int refundsMaxLo;
	@Value("${sn.withdrawal.prefix}")
	private String withdrawalPrefix;
	@Value("${sn.withdrawal.maxLo}")
	private int withdrawalMaxLo;

	public String generate(Type type) {
		Assert.notNull(type);
		if (type == Type.order) {
			return orderHiloOptimizer.generate();
		} else if( type == Type.payment){
			return paymentHiloOptimizer.generate();
		} else if (type == Type.refunds){
			return refundsHiloOptimizer.generate();
		} else if (type == Type.withdrawal) {
			return withdrawalHiloOptimizer.generate();
		}
		return null;
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		orderHiloOptimizer = new HiloOptimizer(Type.order, orderPrefix, orderMaxLo);
		paymentHiloOptimizer = new HiloOptimizer(Type.payment, paymentPrefix, paymentMaxLo);
		refundsHiloOptimizer = new HiloOptimizer(Type.refunds, refundsPrefix, refundsMaxLo);
		withdrawalHiloOptimizer = new HiloOptimizer(Type.withdrawal, withdrawalPrefix, withdrawalMaxLo);
	}

	private long getLastValue(Type type) {
		String sql = "select last_value from smk_sn sn where sn.type = ? for update";
		long lastValue = Db.queryLong(sql, type.toString());

		sql = "update smk_sn sn set last_value = ? where sn.type = ? ";
		Db.update(sql,lastValue+1,type.toString());

		return lastValue;
	}

	/**
	 * 高低位算法
	 */
	private class HiloOptimizer {

		private Type type;
		private String prefix;
		private int maxLo;
		private int lo;
		private long hi;
		private long lastValue;

		public HiloOptimizer(Type type, String prefix, int maxLo) {
			this.type = type;
			this.prefix = prefix != null ? prefix.replace("{", "${") : "";
			this.maxLo = maxLo;
			this.lo = maxLo + 1;
		}

		public synchronized String generate() {
			if (lo > maxLo) {
				lastValue = getLastValue(type);
				lo = lastValue == 0 ? 1 : 0;
				hi = lastValue * (maxLo + 1);
			}
			try {
				return FreemarkerUtils.process(prefix, null) + (hi + lo++);
			} catch (IOException e) {
				e.printStackTrace();
			} catch (TemplateException e) {
				e.printStackTrace();
			}
			return String.valueOf(hi + lo++);
		}
	}
}
